﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Common.Utilities.TextMatch
{
    //Provides a variety of routines for validating objects...
    /// <summary>
    /// 
    /// </summary>
    public class CompositeValidator : Validator
    {
        private Hashtable childValidators;				//The composited Validators.
        private CompositeType joinType;					//The composite type to join the Validators with...


        #region Constructor & Object Overrides


        /// <summary>
        /// 
        /// </summary>
        /// <param name="validators"></param>
        public CompositeValidator(params Validator[] validators) : this(CompositeType.And, validators) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="joinType"></param>
        /// <param name="validators"></param>
        public CompositeValidator(CompositeType joinType, params Validator[] validators)
        {
            //Ensure the parameters are valid
            Debug.Assert(validators != null, "Unexpected null value.", "The CompositeValidator.ctor() method requires a value.");

            if (validators == null)
                throw new ArgumentNullException("validators");

            this.joinType = joinType;
            this.childValidators = new Hashtable(validators.Length);

            foreach (Validator validator in validators)
                this.childValidators.Add(validator.GetHashCode(), validator);
        }


        #endregion


        #region Protected Collection Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected bool Contains(object key)
        {
            //Ensure the parameters are valid
            Debug.Assert(key != null, "Unexpected null value.", "The CompositeValidator.Contains(Object) method requires a value");
            if (key == null)
                throw new ArgumentNullException("key");

            return childValidators.Contains(key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="validator"></param>
        /// <returns></returns>
        protected bool Contains(Validator validator)
        {
            //Ensure the parameters are valid
            Debug.Assert(validator != null, "Unexpected null value.", "The CompositeValidator.Contains(Validator) method requires a value");

            if (validator == null)
                throw new ArgumentNullException("validator");

            return this.childValidators.ContainsValue(validator);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="validators"></param>
        protected void Add(params Validator[] validators)
        {
            //Ensure the parameters are valid
            Debug.Assert(validators != null, "Unexpected null value.", "The CompositeValidator.Add(Validator[]) method requires a value.");

            if (validators == null)
                throw new ArgumentNullException("validators");

            foreach (Validator validator in validators)
                this.childValidators.Add(validator.GetHashCode(), validator);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="validator"></param>
        protected void Add(object key, Validator validator)
        {
            //Ensure the parameters are valid
            Debug.Assert((key != null && validator != null), "Unexpected null value.", "The CompositeValidator.Add(Object, Validator) method requires a value");

            if (key == null)
                throw new ArgumentNullException("key");
            else if (validator == null)
                throw new ArgumentNullException("validator");

            childValidators.Add(key, validator);
        }


        #endregion


        #region Validation Implementations...


        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool Validate(string value)
        {
            //Ensure the parameters are valid
            Debug.Assert(value != null, "Unexpected null value.", "The CompositeValidator.Validate(String) method requires a value");

            if (value == null)
                throw new ArgumentNullException("value");

            return this.NormalizeValidation(ValidateChildValidations(value));
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool Validate(object key, string value)
        {
            //Ensure the parameters are valid
            Debug.Assert((key != null && value != null), "Unexpected null value.", "The CompositeValidator.Validate(Object, String) method requires a value");

            if (key == null)
                throw new ArgumentNullException("key");
            else if (value == null)
                throw new ArgumentNullException("value");

            //Check we have a validator with the key
            if (this.childValidators.ContainsKey(key))
                return ((Validator)this.childValidators[key]).Validate(value);

            return NormalizeValidation(false);
        }


        /// <summary>
        /// Check all the child validators... 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private bool ValidateChildValidations(string value)
        {
            foreach (Validator validator in this.childValidators.Values)
            {
                if (!validator.Validate(value))
                {
                    if (this.joinType == CompositeType.And)
                        return false;
                }
                else if (this.joinType == CompositeType.Or)
                    return true;
            }

            return (this.joinType != CompositeType.Or);
        }

        #endregion

    }
}
